# frozen_string_literal: true

module AssociationPreloader
  class Preloader < GraphQL::Function
    attr_reader :type

    def initialize(
      dependency_field_names = nil,
      resolve_func: nil,
      block_db: false,
      type: nil
    )
      @dependency_field_names = dependency_field_names
      @resolve_func = resolve_func
      @block_db = block_db
      @type = type || 'dummy_value'
    end

    def call(object, args, ctx)
      dependency_field_names = @dependency_field_names || [field_name_sym(ctx)]
      resolve_func = @resolve_func || proc { |obj| obj.send(field_name_sym(ctx)) }

      LoaderImpl.for(*dependency_field_names).load(object).then(proc { |obj| resolve(obj, args, ctx, resolve_func) })
    end

    def resolve(obj, args, ctx, resolve_func)
      if @block_db
        resolve_func.call(obj, args, ctx)
      else
        DbBlocker.with_db_performance_problems { resolve_func.call(obj, args, ctx) }
      end
    end

    private

    def field_name_sym(ctx)
      GraphQL::Schema::Member::BuildType.underscore(ctx.field.name).to_sym
    end
  end

  class LoaderImpl < GraphQL::Batch::Loader
    def initialize(*associations)
      @associations = *associations
    end

    def perform(vals)
      ActiveRecord::Base.connection_pool.with_connection do
        ActiveRecord::Associations::Preloader.new.preload(vals, @associations)
      end
      vals.each { |val| fulfill(val, val) }
    end
  end
end
